<?php
require_once(dirname(__FILE__) . '/config/index.php');

/*
protected $classTranslations = array(
	'ActualClassName' => 'WhatTheClassIsReferredToInThisModel'
);

protected $foreignKeys = array(
	self::USER_ID => "User",
	self::SITE_ID => "Site"
);

protected $hasManyThrough = array(
	'Statuses' => array('Status', 'UserStatus')
);

protected $conditionalAssociations = array(
	'UnreadMessages' => array('Message', array(Message::RECIPIENT_ID => 'ID', Message::READ => false)),
	'ReadMessages' => array('Message', array(Message::RECIPIENT_ID => 'ID', Message::READ => false)),
	'AllMessages' => array('Message', array(Message::RECIPIENT_ID => 'ID'))
);

const DEPENDENT_DESTROY_LINK_TABLES = true
*/

class TestOfOrm extends UnitTestCase {

	protected $data = array();
	
	public function setUp() {
		$this->data['site'] = TestSite::doSelectOrCreate(new TestSite(), array(TestSite::NAME => 'Test Site'));
		$this->data['user'] = TestUser::doSelectOrCreate(new TestUser(), array(TestUser::NAME => 'Test User', TestUser::SITE_ID => $this->data['site']->getPk(), TestUser::AGE => 12));
		$this->data['user_two'] = TestUser::doSelectOrCreate(new TestUser(), array(TestUser::NAME => 'Test User Two', TestUser::SITE_ID => $this->data['site']->getPk(), TestUser::AGE => 12));
		$this->data['friend'] = TestFriend::doSelectOrCreate(new TestFriend(), array(TestFriend::NAME => 'Test Friend'));
		$this->data['user_friend'] = TestUserFriend::doSelectOrCreate(new TestUserFriend(), array(TestUserFriend::TEST_USER_ID => $this->data['user']->getPk(), TestUserFriend::TEST_FRIEND_ID => $this->data['friend']->getPk()));
	}
	
	public function tearDown() {
		$this->data = array();
		TestSite::doTruncate(new TestSite());
		TestUser::doTruncate(new TestUser());
		TestFriend::doTruncate(new TestFriend());
		TestUserFriend::doTruncate(new TestUserFriend());
	}
	
	//CRUD
	
	public function testCreate() {
		$site = new TestSite();
		$site->setName('A Test Site');
		$site->save();
		
		$this->assertNotNull($site);
	}

	public function testCreateFromArray() {
		$user = TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'A Test User', TestUser::SITE_ID => $this->data['site']->getPk()));  
		$this->assertNotNull($user);
	}
	
	public function testReadOne() {
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site');
		$site = TestSite::doSelectOne(new TestSite(), $c);
		
		$this->assertEqual($site->getName(), 'Test Site');
	}
	
	public function testReadMany() {
		TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Another Test Site'));
		
		$sites = TestSite::doSelect(new TestSite(), TestSite::newCriteria());
		
		$this->assertNotEqual(count($sites), 1);
		$this->assertEqual($sites[count($sites)-1]->getName(), 'Another Test Site');		
	}
	
	public function testUpdate() {
		$this->data['site']->setName('My Test Site');
		
		$this->assertEqual($this->data['site']->getName(), 'My Test Site');
	}
	
	public function testDelete() {
		$this->data['site']->delete();
		
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site');
		$site = TestSite::doSelectOne(new TestSite(), $c);
		
		$this->assertFalse($site);
	}
	
	public function testDependentDestroyHasManyThroughRecordsWithConstant() {
		$user_id = $this->data['user']->getPk();
		$this->data['user']->delete();
		
		$c = TestUserFriend::newCriteria();
		$c->add(TestUserFriend::TEST_USER_ID, $user_id);
		$through_record = TestUserFriend::doSelectOne(new TestUserFriend(), $c);
		
		$this->assertFalse($through_record);		
	}

	public function testDependentDestroyHasManyThroughRecordsWithAssociationFlag() {
		$friend_id = $this->data['friend']->getPk();
		$this->data['friend']->delete();
		
		$c = TestUserFriend::newCriteria();
		$c->add(TestUserFriend::TEST_FRIEND_ID, $friend_id);
		$through_record = TestUserFriend::doSelectOne(new TestUserFriend(), $c);
		
		$this->assertFalse($through_record);		
	}

	public function testClassTranslations() {
		$users = $this->data['site']->getTranslatedUser();
		$this->assertEqual(count($users), 2);
		$this->assertEqual($users[0]->getName(), 'Test User');
		$this->assertEqual($users[1]->getName(), 'Test User Two');
	}
	
	public function testConditionalAssociations() {
		$users_no_conditions = $this->data['site']->getUsersNoConditions();
		$users_called_test = $this->data['site']->getUsersCalledTest();
		
		$this->assertEqual(count($users_no_conditions), 2);
		$this->assertEqual($users_no_conditions[0]->getName(), 'Test User');
		$this->assertEqual($users_no_conditions[1]->getName(), 'Test User Two');

		$this->assertEqual(count($users_called_test), 1);
		$this->assertEqual($users_called_test[0]->getName(), 'Test User');
	}

	public function testDateAdd() {
		$c = TestSite::newCriteria();
		$c->add(Nina_Orm_Criteria::dateAdd(TestSite::CREATED_AT, 2, 'SECOND'), Nina_Timestamp::now(), Nina_Orm_Criteria::GREATER_THAN);
		$this->assertEqual(TestSite::doCount(new TestSite(), $c), 1);

		$c = TestSite::newCriteria();
		$c->add(Nina_Orm_Criteria::dateAdd(TestSite::CREATED_AT, 2, 'SECOND'), Nina_Timestamp::now(), Nina_Orm_Criteria::LESS_THAN);
		$this->assertEqual(TestSite::doCount(new TestSite(), $c), 0);
	}
	
	public function testDateSub() {
		$c = TestSite::newCriteria();
		$c->add(Nina_Orm_Criteria::dateSub(TestSite::CREATED_AT, 2, 'SECOND'), Nina_Timestamp::now(), Nina_Orm_Criteria::LESS_THAN);
		$this->assertEqual(TestSite::doCount(new TestSite(), $c), 1);

		$c = TestSite::newCriteria();
		$c->add(Nina_Orm_Criteria::dateSub(TestSite::CREATED_AT, 2, 'SECOND'), Nina_Timestamp::now(), Nina_Orm_Criteria::GREATER_THAN);
		$this->assertEqual(TestSite::doCount(new TestSite(), $c), 0);
	}
	
	public function testMin() {
		$c = TestUser::newCriteria();
		$c->setMin(TestUser::ID);
		$c->addGroupBy(TestUser::SITE_ID);
		$result = TestUser::doSelectOne(new TestUser(), $c);
		$this->assertEqual($result->getId(), 1);
	}
	
	public function testMax() {
		$c = TestUser::newCriteria();
		$c->setMax(TestUser::ID);
		$c->addGroupBy(TestUser::SITE_ID);
		$result = TestUser::doSelectOne(new TestUser(), $c);
		$this->assertEqual($result->getId(), 2);
	}
	
	public function testLower() {
		$c = TestUser::newCriteria();
		$c->add(Nina_Orm_Criteria::lower(TestUser::NAME), 'test user');
		$this->assertEqual(TestUser::doCount(new TestUser(), $c), 1);

		$c = TestUser::newCriteria();
		$c->add(TestUser::NAME, 'test user');
		$this->assertEqual(TestUser::doCount(new TestUser(), $c), 0);
	}
	
	public function testUpper() {
		$c = TestUser::newCriteria();
		$c->add(Nina_Orm_Criteria::upper(TestUser::NAME), 'TEST USER');
		$this->assertEqual(TestUser::doCount(new TestUser(), $c), 1);

		$c = TestUser::newCriteria();
		$c->add(TestUser::NAME, 'TEST USER');
		$this->assertEqual(TestUser::doCount(new TestUser(), $c), 0);
	}
	
	public function testCountField() {
		$c = TestUser::newCriteria();
		$c->setCount(TestUser::ID);
		$c->addGroupBy(TestUser::SITE_ID);
		$result = TestUser::doSelectOne(new TestUser(), $c);
		$this->assertEqual($result->getId(), 2);

		$c = TestUser::newCriteria();
		$c->setCount(TestUser::ID);
		$c->addGroupBy(TestUser::NAME);
		$result = TestUser::doSelectOne(new TestUser(), $c);		
		$this->assertEqual($result->getId(), 1);
	}
	
	public function testSumField() {
		$c = TestUser::newCriteria();
		$c->setSum(TestUser::SEQUENCE);
		$result = TestUser::doSelectOne(new TestUser(), $c);		
		$this->assertEqual($result->getSEQUENCE(), 3);
	}
	
	public function testSetSequence() {
	
		$site1 = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Sequence Test Site 1'));
		$site2 = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Sequence Test Site 2'));
		
		$this->assertTrue(is_numeric($site1->getSequence())); 
		$this->assertTrue(is_numeric($site2->getSequence()));
		$this->assertWithinMargin($site1->getSequence(), $site2->getSequence(), 1);   	
	}
	
	public function testSetCreatedDate() {
	
		$created_at1 = $this->data['site']->getCreatedAt();
		sleep(1);
		$this->data['site']->setName('Test Site Edited');
		$this->data['site']->save();
		
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site Edited');
		$site = TestSite::doSelectOne(new TestSite(), $c);
		
		$created_at2 = $site->getCreatedAt();
		
		$this->assertNotNull($created_at1);
		$this->assertEqual($created_at1, $created_at2);
	}
	
	public function testSetUpdatedDate() {
		
		$updated_at1 = $this->data['site']->getUpdatedAt();
		sleep(1);
		$this->data['site']->setName('Test Site Edited');
		$this->data['site']->save();
		
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site Edited');
		$site = TestSite::doSelectOne(new TestSite(), $c);
		
		$updated_at2 = $site->getUpdatedAt();
		
		$this->assertNotNull($updated_at1);
		$this->assertNotNull($updated_at2);
		$this->assertNotEqual($updated_at1, $updated_at2);
	}
	
	public function testCount() {
	
		$this->assertEqual(TestSite::doCount(new TestSite(), TestSite::newCriteria()), 1);		
		$site = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Count Test Site'));		
		$this->assertEqual(TestSite::doCount(new TestSite(), TestSite::newCriteria()), 2);
	}
	
	public function testTruncate() {
	
		TestSite::doTruncate(new TestSite());
		$this->assertEqual(TestSite::doCount(new TestSite(), TestSite::newCriteria()), 0);
	}
	
	//Searching
	
	public function testFind() {
	
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site');
		$site = TestSite::doSelect(new TestSite(), $c);
		$this->assertEqual($this->data['site']->getPk(), $site[0]->getPk());
	
		$c = TestSite::newCriteria();
		$c->add(TestSite::NAME, 'Test Site');
		$site = TestSite::doSelectOne(new TestSite(), $c);
		$this->assertEqual($this->data['site']->getPk(), $site->getPk());		
		
		
		//TEST TO ADD
		//joins
		//different criteria
	}
	
	//Associations
	
	public function testReadHasManyAssociation() {
	
		$users = $this->data['site']->getTestUser();
		$this->assertEqual($this->data['user']->getSite()->getPk(), $this->data['site']->getPk());
		$this->assertEqual($users[0]->getPk(), $this->data['user']->getPk());
	}
	
	public function testReadHasManyThrough() {
	#	$friends = $this->data['user']->getFriends();
	#	$this->assertEqual($friends[0]->getPk(), $this->data['friend']->getPk());
	}
	
	//Ordering
	
	public function testOrder() {
		
	}
	
	public function testGroup() {
	
	}
	
	public function testSequence() {
		
		$ts1 = $this->data['site'];
		$ts2 = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Another Test Site'));
		$ts3 = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'And Another Test Site'));
		$ts4 = TestSite::doInsert(new TestSite(), array(TestSite::NAME => 'Yet Another Test Site'));

		$ts4->moveUp();
		$ts2->moveDown();
		
		$ts1->reload();
		$ts2->reload();
		$ts3->reload();
		$ts4->reload();
		
		$this->assertEqual($ts1->getSequence(), 1);
		$this->assertEqual($ts2->getSequence(), 3);
		$this->assertEqual($ts3->getSequence(), 4);
		$this->assertEqual($ts4->getSequence(), 2);
		
		//Test we order by sequence by default
		$sites = TestSite::doSelect(new TestSite(), TestSite::newCriteria());
		
		$i = 1;
		foreach($sites as $site) {
			$this->assertEqual($site->getSequence(), $i++);
		}
	}
	
	public function testSequenceWithCriteria() {
	
		$tu1 = $this->data['user'];
		$tu2 = TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'Another Test User', TestUser::SITE_ID => 2));
		$tu3 = TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'And Another Test User', TestUser::SITE_ID => 1));
		$tu4 = TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'Yet Another Test User', TestUser::SITE_ID => 2));
		
		$c1 = TestUser::newCriteria();
		$c1->add(TestUser::SITE_ID, 1);
		$c2 = TestUser::newCriteria();
		$c2->add(TestUser::SITE_ID, 2);
		
		$tu1->moveDown($c1);
		$tu4->moveUp($c2);
		
		$tu1->reload();
		$tu2->reload();
		$tu3->reload();
		$tu4->reload();
		
		$this->assertEqual($tu1->getSequence(), 3);
		$this->assertEqual($tu2->getSequence(), 4);
		$this->assertEqual($tu3->getSequence(), 1);
		$this->assertEqual($tu4->getSequence(), 2);		
	}
	
	//Validations
	
	public function testValidatePresence() {
		$user = new TestUser();
		$user->setName('');
		
		$this->assertFalse($user->valid());
		$this->assertEqual($user->errors(), array('presence' => array(TestUser::NAME)));		
		
		$this->assertFalse(TestUser::doInsert(new TestUser(), array(TestUser::NAME => '')));
	}
	
	public function testValidateNumber() {
		$user = new TestUser();
		$user->setName('Another Test User');
		$user->setAge('twelve');
		
		$this->assertFalse($user->valid());
		$this->assertEqual($user->errors(), array('number' => array(TestUser::AGE)));
		
		$this->assertFalse(TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'Another Test User', TestUser::AGE => 'twelve')));
	}
	
	public function testValidateLength() {
		$user = new TestUser();
		$user->setName('TU');
		
		$this->assertFalse($user->valid());
		$this->assertEqual($user->errors(), array('length' => array(TestUser::NAME)));
		
		$this->assertFalse(TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'TU')));
	}
	
	public function testValidateUnique() {
		$user = new TestUser();
		$user->setName('Test User');
		
		$this->assertFalse($user->valid());
		$this->assertEqual($user->errors(), array('unique' => array(TestUser::NAME)));
		
		$this->assertFalse(TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'Test User')));
	}
	
	public function testValidatePass() {
		$user = new TestUser();
		$user->setName('Another Test User');
		$user->setAge(12);
		
		$this->assertTrue($user->valid());
		$this->assertEqual($user->errors(), array());
		
		$this->assertTrue(TestUser::doInsert(new TestUser(), array(TestUser::NAME => 'Another Test User', TestUser::AGE => 12)));
	}
}
